/*
 *    Copyright (c) 2018-2025, boss All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the boss developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: boss
 */
package com.hopes.bizz.close.visitor.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.hopes.bizz.close.basic.entity.ClosedGpsDeviceEntity;
import com.hopes.bizz.close.basic.service.ClosedGpsDeviceService;
import com.hopes.bizz.close.basic.service.ClosedOperateAuditRecordService;
import com.hopes.bizz.close.common.constant.BasicDicConstant;
import com.hopes.bizz.close.common.constant.CacheConstants;
import com.hopes.bizz.close.common.constant.CloseModuleEnums;
import com.hopes.bizz.close.common.constant.VisitorDicConstant;
import com.hopes.bizz.close.common.constant.enums.ParkCloseSysDictEnum;
import com.hopes.bizz.close.hazard.entity.ClosedHazardCarEntity;
import com.hopes.bizz.close.visitor.dto.ClosedHazardCarApplyDTO;
import com.hopes.bizz.close.visitor.dto.ClosedHazardCardBindGpsDTO;
import com.hopes.bizz.close.visitor.dto.ClosedVisitorApplyAuditDTO;
import com.hopes.bizz.close.visitor.entity.ClosedApplyGoodsRelEntity;
import com.hopes.bizz.close.visitor.entity.ClosedHazardCarApplyEntity;
import com.hopes.bizz.close.visitor.dto.ClosedHazardCarApplyExcelDTO;
import com.hopes.bizz.close.visitor.service.ClosedApplyGoodsRelService;
import com.hopes.bizz.close.visitor.vo.ClosedHazardCarApplyExcelVO;
import com.hopes.bizz.close.visitor.mapper.ClosedHazardCarApplyMapper;
import com.hopes.bizz.close.visitor.service.ClosedHazardCarApplyService;
import com.hopes.bizz.close.visitor.vo.ClosedHazardCarApplyVO;
import com.hopes.bizz.common.core.constant.CommonConstant;
import com.hopes.boss.admin.api.resolver.DictResolver;
import com.hopes.boss.admin.api.resolver.enums.AdminDictEnum;
import com.hopes.boss.admin.api.resolver.enums.SysDictEnum;
import com.hopes.boss.common.core.exception.BizException;
import com.hopes.boss.common.entity.ValidateImportBO;
import com.hopes.boss.common.excel.vo.ErrorMessage;
import com.hopes.boss.common.security.util.SecurityUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 危险品车辆预约
 *
 * @author pig
 * @date 2024-01-26 14:34:05
 */
@Service
@RequiredArgsConstructor
public class ClosedHazardCarApplyServiceImpl extends ServiceImpl<ClosedHazardCarApplyMapper, ClosedHazardCarApplyEntity> implements ClosedHazardCarApplyService {

    private final ClosedGpsDeviceService closedGpsDeviceService;
    private final ClosedApplyGoodsRelService closedApplyGoodsRelService;
    private final ClosedOperateAuditRecordService closedOperateAuditRecordService;

    @Override
    public boolean save(ClosedHazardCarApplyEntity entity) {
        entity.setApplyUserId(SecurityUtils.getUserId());
        if (ObjUtil.isNull(entity.getApplyTime())) {
            entity.setApplyTime(Calendar.getInstance().getTime());
        }
        entity.setStatus(VisitorDicConstant.CLOSE_HAZARD_CAR_APPLY_STATUS);
        entity.setAuditStatus(VisitorDicConstant.CLOSE_VISITOR_AUDIT_STATUS_NOT_SUBMIT);
        return SqlHelper.retBool(getBaseMapper().insert(entity));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean submit(Long id) {
        // 1、修改状态
        lambdaUpdate()
                .set(ClosedHazardCarApplyEntity::getAuditStatus, BasicDicConstant.CLOSE_VISITOR_AUDIT_STATUS_NOT_AUDIT)
                .eq(ClosedHazardCarApplyEntity::getId, id).update();
        // 2、创建提交日志记录
        return closedOperateAuditRecordService.buildSaveOperateAuditRecord(id,
                CloseModuleEnums.GeneralCarOperateEnum.submit.getValue(),
                BasicDicConstant.CLOSE_VISITOR_AUDIT_STATUS_NOT_AUDIT, null, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveAndSubmit(ClosedHazardCarApplyEntity closedHazardCarApply) {
        // 1、保存危险品预约信息
        closedHazardCarApply.setApplyUserId(SecurityUtils.getUserId());
        if (ObjUtil.isNull(closedHazardCarApply.getApplyTime())) {
            closedHazardCarApply.setApplyTime(Calendar.getInstance().getTime());
        }
        closedHazardCarApply.setStatus(VisitorDicConstant.CLOSE_HAZARD_CAR_APPLY_STATUS);
        closedHazardCarApply.setAuditStatus(VisitorDicConstant.CLOSE_VISITOR_AUDIT_STATUS_NOT_AUDIT);
        // 1、重新了save方法，不可使用saveOrUpdate方法
        if (ObjUtil.isNull(closedHazardCarApply.getId())) {
            getBaseMapper().insert(closedHazardCarApply);
        } else {
            getBaseMapper().updateById(closedHazardCarApply);
        }
        // 2、创建&& 提交日志
        return closedOperateAuditRecordService.buildSaveOperateAuditRecord(closedHazardCarApply.getId(),
                CloseModuleEnums.GeneralCarOperateEnum.audit.getValue(),
                VisitorDicConstant.CLOSE_VISITOR_AUDIT_STATUS_NOT_AUDIT, null, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean auditById(ClosedVisitorApplyAuditDTO closedVisitorApplyAuditDTO) {
        // 1、修改对象
        lambdaUpdate()
                .set(ClosedHazardCarApplyEntity::getRemark, closedVisitorApplyAuditDTO.getRemark())
                .set(ClosedHazardCarApplyEntity::getAuditStatus, closedVisitorApplyAuditDTO.getAuditStatus())
                .set(ClosedHazardCarApplyEntity::getAuditStatusDesc, closedVisitorApplyAuditDTO.getAuditStatusDesc())
                .eq(ClosedHazardCarApplyEntity::getId, closedVisitorApplyAuditDTO.getId()).update();
        // 2、创建 && 提交日志
        return closedOperateAuditRecordService.buildSaveOperateAuditRecord(
                closedVisitorApplyAuditDTO.getId(),
                CloseModuleEnums.GeneralCarOperateEnum.audit.getValue(),
                closedVisitorApplyAuditDTO.getAuditStatus(), closedVisitorApplyAuditDTO.getAuditStatusDesc(), closedVisitorApplyAuditDTO.getRemark());
    }

    @Override
    @Cacheable(value = CacheConstants.CLOSE_HAZARD_APPLY + "#60", unless = "#result.isEmpty()")
    public List<ClosedHazardCarApplyEntity> queryBindGpsDeviceList() {
        final LambdaQueryWrapper<ClosedHazardCarApplyEntity> wrapper = Wrappers.<ClosedHazardCarApplyEntity>lambdaQuery()
                .isNotNull(ClosedHazardCarApplyEntity::getGpsDeviceNo);
        return getBaseMapper().selectList(wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean operateGpsDevice(ClosedHazardCardBindGpsDTO closedHazardCardBindGpsDTO) {
        if (ObjUtil.isNull(closedHazardCardBindGpsDTO.getHazardCarGpsEnum())) {
            throw new BizException("Gps设备操作类型不可为空");
        }
        if (ObjUtil.isNull(closedHazardCardBindGpsDTO.getHazardApplyId())) {
            throw new BizException("危险品车辆不可为空");
        }
        if (ObjUtil.isNull(closedHazardCardBindGpsDTO.getGpsDeviceNo())) {
            throw new BizException("Gps设备不可为空");
        }

        if (closedHazardCardBindGpsDTO.getHazardCarGpsEnum().equals(CloseModuleEnums.HazardCarGpsEnum.bind)) {
            // 1、绑定GPS设备
            lambdaUpdate().set(ClosedHazardCarApplyEntity::getGpsDeviceNo, closedHazardCardBindGpsDTO.getGpsDeviceNo())
                    .eq(ClosedHazardCarApplyEntity::getId, closedHazardCardBindGpsDTO.getHazardApplyId()).update();
            // 2、查询对象
            final ClosedHazardCarApplyEntity applyEntity = getBaseMapper().selectById(closedHazardCardBindGpsDTO.getHazardApplyId());
            // 3、修改GPS设备为绑定状态
            closedGpsDeviceService.lambdaUpdate()
                    .set(ClosedGpsDeviceEntity::getBindFlag, CommonConstant.boolean_true_string)
                    .set(ClosedGpsDeviceEntity::getLogisticsBillId, applyEntity.getId())
                    .set(ClosedGpsDeviceEntity::getLogisticsBillNo, applyEntity.getBillNo())
                    .eq(ClosedGpsDeviceEntity::getDeviceNo, closedHazardCardBindGpsDTO.getGpsDeviceNo()).update();
        } else {
            // 1、解除GPS设备绑定
            lambdaUpdate().set(ClosedHazardCarApplyEntity::getGpsDeviceNo, null)
                    .eq(ClosedHazardCarApplyEntity::getId, closedHazardCardBindGpsDTO.getHazardApplyId()).update();
            // 2、修改Gps设备的绑定状态
            closedGpsDeviceService.lambdaUpdate()
                    .set(ClosedGpsDeviceEntity::getBindFlag, CommonConstant.boolean_false_string)
                    .set(ClosedGpsDeviceEntity::getLogisticsBillId, null).set(ClosedGpsDeviceEntity::getLogisticsBillNo, null)
                    .eq(ClosedGpsDeviceEntity::getDeviceNo, closedHazardCardBindGpsDTO.getGpsDeviceNo()).update();
        }
        return true;
    }

    @Override
    public ClosedHazardCarApplyVO getVoById(Long id) {
        final ClosedHazardCarApplyEntity closedHazardCarApplyEntity = getBaseMapper().selectById(id);
        if (ObjUtil.isNull(closedHazardCarApplyEntity)) {
            return new ClosedHazardCarApplyVO();
        }
        final List<ClosedApplyGoodsRelEntity> closedApplyGoodsRelList = closedApplyGoodsRelService.list(
                Wrappers.<ClosedApplyGoodsRelEntity>lambdaQuery().eq(ClosedApplyGoodsRelEntity::getApplyId, id));
        final ClosedHazardCarApplyVO closedHazardCarApplyVO = new ClosedHazardCarApplyVO();
        BeanUtils.copyProperties(closedHazardCarApplyEntity, closedHazardCarApplyVO);
        closedHazardCarApplyVO.setClosedApplyGoodsRelList(closedApplyGoodsRelList);
        return closedHazardCarApplyVO;
    }

    @Override
    public ClosedHazardCarApplyVO getVoByBillNo(String billNo) {
        if (StrUtil.isBlank(billNo)) {
            return new ClosedHazardCarApplyVO();
        }
        final ClosedHazardCarApplyEntity closedHazardCarApply = this.getOne(
                Wrappers.<ClosedHazardCarApplyEntity>lambdaQuery().eq(ClosedHazardCarApplyEntity::getBillNo, billNo).orderByDesc(ClosedHazardCarApplyEntity::getApplyTime).last("limit 1"));
        if (ObjUtil.isNull(closedHazardCarApply)) {
            return new ClosedHazardCarApplyVO();
        }
        final List<ClosedApplyGoodsRelEntity> closedApplyGoodsRelList = closedApplyGoodsRelService.list(
                Wrappers.<ClosedApplyGoodsRelEntity>lambdaQuery().eq(ClosedApplyGoodsRelEntity::getApplyId, closedHazardCarApply.getId()));
        final ClosedHazardCarApplyVO closedHazardCarApplyVO = new ClosedHazardCarApplyVO();
        closedHazardCarApplyVO.setId(closedHazardCarApply.getId());
        closedHazardCarApplyVO.setHazardType(closedHazardCarApply.getHazardType());
        closedHazardCarApplyVO.setClosedApplyGoodsRelList(closedApplyGoodsRelList);
        return closedHazardCarApplyVO;
    }


    @Override
    public IPage<ClosedHazardCarApplyVO> pageVO(Page page, LambdaQueryWrapper<ClosedHazardCarApplyEntity> wrapper) {

        final Page<ClosedHazardCarApplyEntity> selectPage = getBaseMapper().selectPage(page, wrapper);
        final List<Long> applyIdList = selectPage.getRecords().stream().map(ClosedHazardCarApplyEntity::getId).collect(Collectors.toList());
        final Map<Long, List<ClosedApplyGoodsRelEntity>> longListMap = closedApplyGoodsRelService.list(
                Wrappers.<ClosedApplyGoodsRelEntity>lambdaQuery().in(ClosedApplyGoodsRelEntity::getApplyId, applyIdList)).stream().collect(Collectors.groupingBy(ClosedApplyGoodsRelEntity::getApplyId));
        final List<ClosedHazardCarApplyVO> collect = selectPage.getRecords().stream().map(e -> {
            final ClosedHazardCarApplyVO applyVO = new ClosedHazardCarApplyVO();
            BeanUtils.copyProperties(e, applyVO);
            applyVO.setClosedApplyGoodsRelList(longListMap.getOrDefault(e.getId(), new ArrayList<>()));
            return applyVO;
        }).collect(Collectors.toList());

        final Page<ClosedHazardCarApplyVO> voPage = new Page<>(selectPage.getCurrent(), selectPage.getSize(), selectPage.getTotal(), selectPage.searchCount());
        voPage.setRecords(collect);
        return voPage;
    }

    @Override
    public IPage<ClosedHazardCarApplyEntity> queryAppHazardCarApplySelfPage(Page page, ClosedHazardCarApplyDTO closedHazardCarApplyDTO) {
        final LambdaQueryWrapper<ClosedHazardCarApplyEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ClosedHazardCarApplyEntity::getApplyUserId, SecurityUtils.getUserId())
                .eq(ObjUtil.isNotNull(closedHazardCarApplyDTO.getOrgId()), ClosedHazardCarApplyEntity::getOrgId, closedHazardCarApplyDTO.getOrgId())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getCompanyCar()), ClosedHazardCarApplyEntity::getCompanyCar, closedHazardCarApplyDTO.getCompanyCar())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getEmptyCar()), ClosedHazardCarApplyEntity::getEmptyCar, closedHazardCarApplyDTO.getEmptyCar())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getHazardType()), ClosedHazardCarApplyEntity::getHazardType, closedHazardCarApplyDTO.getHazardType())
                .eq(ObjUtil.isNotNull(closedHazardCarApplyDTO.getAuditStatus()), ClosedHazardCarApplyEntity::getAuditStatus, closedHazardCarApplyDTO.getAuditStatus())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getStatus()), ClosedHazardCarApplyEntity::getStatus, closedHazardCarApplyDTO.getStatus())
                .ge(ObjUtil.isNotNull(closedHazardCarApplyDTO.getStartTime()), ClosedHazardCarApplyEntity::getPlanEnterTime, closedHazardCarApplyDTO.getStartTime())
                .le(ObjUtil.isNotNull(closedHazardCarApplyDTO.getEndTime()), ClosedHazardCarApplyEntity::getPlanLeaveTime, closedHazardCarApplyDTO.getEndTime())
                .like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getLicencePlateNum, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getBillNo, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getDriverName, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getAcceptName, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getSupercargoName, closedHazardCarApplyDTO.getKeyword());
        wrapper.orderByDesc(ClosedHazardCarApplyEntity::getApplyTime);
        return this.page(page, wrapper);
    }

    @Override
    public IPage<ClosedHazardCarApplyEntity> queryAppHazardCarApplyAuditPage(Page page, List<Integer> auditStatusList, ClosedHazardCarApplyDTO closedHazardCarApplyDTO) {
        final LambdaQueryWrapper<ClosedHazardCarApplyEntity> wrapper = Wrappers.lambdaQuery();
        wrapper.in(CollUtil.isNotEmpty(auditStatusList), ClosedHazardCarApplyEntity::getAuditStatus, auditStatusList)
                .eq(ObjUtil.isNotNull(closedHazardCarApplyDTO.getOrgId()), ClosedHazardCarApplyEntity::getOrgId, closedHazardCarApplyDTO.getOrgId())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getCompanyCar()), ClosedHazardCarApplyEntity::getCompanyCar, closedHazardCarApplyDTO.getCompanyCar())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getEmptyCar()), ClosedHazardCarApplyEntity::getEmptyCar, closedHazardCarApplyDTO.getEmptyCar())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getHazardType()), ClosedHazardCarApplyEntity::getHazardType, closedHazardCarApplyDTO.getHazardType())
                .eq(ObjUtil.isNotNull(closedHazardCarApplyDTO.getAuditStatus()), ClosedHazardCarApplyEntity::getAuditStatus, closedHazardCarApplyDTO.getAuditStatus())
                .eq(StrUtil.isNotBlank(closedHazardCarApplyDTO.getStatus()), ClosedHazardCarApplyEntity::getStatus, closedHazardCarApplyDTO.getStatus())
                .ge(ObjUtil.isNotNull(closedHazardCarApplyDTO.getStartTime()), ClosedHazardCarApplyEntity::getPlanEnterTime, closedHazardCarApplyDTO.getStartTime())
                .le(ObjUtil.isNotNull(closedHazardCarApplyDTO.getEndTime()), ClosedHazardCarApplyEntity::getPlanLeaveTime, closedHazardCarApplyDTO.getEndTime())
                .like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getLicencePlateNum, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getBillNo, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getDriverName, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getAcceptName, closedHazardCarApplyDTO.getKeyword())
                .or().like(StrUtil.isNotBlank(closedHazardCarApplyDTO.getKeyword()), ClosedHazardCarApplyEntity::getSupercargoName, closedHazardCarApplyDTO.getKeyword());
        wrapper.orderByDesc(ClosedHazardCarApplyEntity::getApplyTime);
        return this.page(page, wrapper);
    }

    @Override
    public ValidateImportBO<ClosedHazardCarApplyExcelDTO> validateImport(List<ClosedHazardCarApplyExcelDTO> excelDTOList) {
        ValidateImportBO<ClosedHazardCarApplyExcelDTO> bo = new ValidateImportBO<>();
        List<ErrorMessage> errorMessageList = new ArrayList<>();
        List<ClosedHazardCarApplyExcelDTO> saveList = new ArrayList<>(excelDTOList);
        int successNum = excelDTOList.size();
        int failedNum = 0;

        bo.setErrorMessages(errorMessageList);
        bo.setSaveList(saveList);
        bo.setSuccessNum(successNum);
        bo.setFailedNum(failedNum);
        return bo;
    }

    @Override
    public ClosedHazardCarApplyEntity importDtoToEntity(ClosedHazardCarApplyExcelDTO excelDto) {
        ClosedHazardCarApplyEntity entity= new ClosedHazardCarApplyEntity();
        BeanUtils.copyProperties(excelDto, entity);
        return entity;
    }

    @Override
    public ClosedHazardCarApplyExcelVO exportEntityToVo(ClosedHazardCarApplyEntity entity) {
        ClosedHazardCarApplyExcelVO excelVO = new ClosedHazardCarApplyExcelVO();
        BeanUtils.copyProperties(entity, excelVO);

        excelVO.setOrgIdStr(DictResolver.getItemLabelByValue(AdminDictEnum.sys_company, entity.getOrgId()));
        excelVO.setEmptyCarStr(DictResolver.getItemLabelByValue(SysDictEnum.yes_no_type, entity.getEmptyCar()));
        excelVO.setCompanyCarStr(DictResolver.getItemLabelByValue(SysDictEnum.yes_no_type, entity.getCompanyCar()));
        excelVO.setStatusStr(DictResolver.getItemLabelByValue(ParkCloseSysDictEnum.close_flow_bill_status, entity.getStatus()));
        excelVO.setHazardTypeStr(DictResolver.getItemLabelByValue(ParkCloseSysDictEnum.close_flow_bill_hazard_type, entity.getHazardType()));
        excelVO.setAuditStatusStr(DictResolver.getItemLabelByValue(ParkCloseSysDictEnum.close_flow_bill_audit_status, entity.getAuditStatus()));
        return excelVO;
    }


}
